/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.ant.taskdefs;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.eclipse.persistence.tools.workbench.ant.AntExtensionBundle;
import org.eclipse.persistence.tools.workbench.framework.resources.DefaultStringRepository;
import org.eclipse.persistence.tools.workbench.framework.resources.StringRepository;
/**
* Base Ant task for projects.
* Runs this task in the current VM. Classpath can be specified using
* a nested element <code>classpath</code>
*/
public abstract class ProjectTask extends Task {
private CommandlineJava commandline;
private AntClassLoader classLoader;
private String userClasspath;
protected StringRepository stringRepository;
protected ProjectTask() {
this.initialize();
}
protected void initialize() {
this.classLoader = null;
this.userClasspath = "";
this.stringRepository = new DefaultStringRepository( AntExtensionBundle.class);
}
/**
* This method is called from execute(), prior excecution of custom execute method.
* Permits to validate this task attributes before execution.
*/
protected void preExecute() throws BuildException {
// do nothing by default;
}
public void execute() throws BuildException {
this.preExecute();
}
protected int execute( Object[] args) throws BuildException {
try {
log( this.stringRepository.getString( "usingSysProperties", System.getProperties().toString()), Project.MSG_VERBOSE);
createClassLoader();
if( classLoader != null) {
classLoader.setThreadContextLoader();
}
TaskRunner runner = new TaskRunner( classLoader);
return runner.execute( args);
}
catch( InvocationTargetException ie) {
log( ie.getTargetException().toString(), Project.MSG_ERR);
throw new BuildException( ie.getTargetException());
}
catch( Throwable e) {
Throwable t = ( e.getCause() == null) ? e : e.getCause();
log( t.toString(), Project.MSG_ERR);
throw new BuildException( t);
}
finally {
if( classLoader != null) {
classLoader.resetThreadContextLoader();
}
}
}
protected abstract String getProjectRunnerClassName();
/**
* Returns the Java command line.
*/
protected CommandlineJava getCommandline() {
if ( commandline == null) {
commandline = new CommandlineJava();
}
return commandline;
}
/**
* Adds path to classpath used for running MW.
*
* @return reference to the classpath in the embedded java command line
*/
public Path createClasspath() {
return getCommandline().createClasspath( getProject()).createPath();
}
/**
* Creates and configures an AntClassLoader instance from the
* nested classpath element.
*/
private void createClassLoader() {
Path commandlineClasspath = getCommandline().getClasspath();
if( commandlineClasspath != null) {
if( classLoader == null) {
Path classpath = ( Path)commandlineClasspath.clone();
if( this.userClasspath.length() > 0) {
Path path = new Path( getProject(), this.userClasspath);
classpath.append( path);
}
classLoader = getProject().createClassLoader( classpath);
classLoader.setParentFirst( false);
classLoader.addJavaLibraries();
log( this.stringRepository.getString( "usingClasspath", classLoader.getClasspath()), Project.MSG_VERBOSE);
}
}
}
/**
* Set the classpath to be used when running this task
*
* @param s an Ant Path object containing the classpath.
*/
public void setClasspath( Path s) {
createClasspath().append( s);
}
/**
* Classpath to use, by reference.
*
* @param reference a reference to an existing classpath
*/
public void setClasspathRef( Reference reference) {
createClasspath().setRefid( reference);
}
protected String getUserClasspath() {
return this.userClasspath;
}
protected void setUserClasspath( String userClasspath) {
this.userClasspath = userClasspath;
}
/**
* Loads and runs the task specified by getProjectRunnerClassName() with the given classloader.
*/
private class TaskRunner {
private Object mappingsRunner;
protected StringRepository stringRepository;
private TaskRunner( ClassLoader classLoader) {
this.initialize( classLoader);
}
private void initialize( ClassLoader classLoader) {
this.stringRepository = new DefaultStringRepository( AntExtensionBundle.class);
this.mappingsRunner = loadMappingsRunner( classLoader);
}
private Object loadMappingsRunner( ClassLoader classLoader) {
Class mappingRunnerClass = null;
Object runner = null;
try {
mappingRunnerClass = Class.forName( getProjectRunnerClassName(), true, classLoader);
}
catch( ClassNotFoundException e) {
throw new RuntimeException( this.stringRepository.getString( "errorWhileExporting", getProjectRunnerClassName()), e);
}
try {
runner = mappingRunnerClass.newInstance();
}
catch( InstantiationException ie) {
throw new RuntimeException( this.stringRepository.getString( "instantiationExceptionAtInstantiation", mappingRunnerClass.getName()), ie);
}
catch( IllegalAccessException iae) {
throw new RuntimeException( this.stringRepository.getString( "illegalAccessExceptionAtInstantiation", mappingRunnerClass.getName()), iae);
}
return runner;
}
/**
* Look up and run the method execute of MappingsRunner.
* @throws InvocationTargetException
*/
private int execute( Object[] args) throws InvocationTargetException {
Method task = null;
Integer result;
try {
Class[] parameters = new Class[ args.length];
for( int i = 0; i < args.length; i++) {
if( args[ i] == null) {
throw new IllegalArgumentException( this.stringRepository.getString( "executeMethodCannotBeNull"));
}
parameters[ i] = args[ i].getClass();
}
task = mappingsRunner.getClass().getMethod( "execute", parameters);
if( task.getReturnType().isAssignableFrom( Integer.class)) {
throw new RuntimeException( this.stringRepository.getString( "expectIntegerReturningType", task.getName()));
}
}
catch ( NoSuchMethodException e) {
throw new RuntimeException( this.stringRepository.getString( "executeMethodNotFound", mappingsRunner.getClass().getName()), e);
}
try {
result = ( Integer)task.invoke( mappingsRunner, args);
}
catch( IllegalArgumentException e) {
throw new RuntimeException( this.stringRepository.getString( "illegalArgumentExceptionAtInvocation", task.getName()), e);
}
catch( IllegalAccessException iae) {
throw new RuntimeException( this.stringRepository.getString( "illegalAccessExceptionAtInvocation", task.getName()), iae);
}
return result.intValue();
}
}
}